package main

import (
	"context"
	"crypto/x509"
	"io"
	"log"
	"net/http"
	"os"
	"protobuf-learn/proto"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func main() {
	// load tls from public CA
	//tlsCredentials := credentials.NewTLS(&tls.Config{})

	// load tls from privite CA
	certPool := x509.NewCertPool()
	cert, err := os.ReadFile("certs/ca.crt")
	if err != nil {
		log.Fatal(err)
	}
	if ok := certPool.AppendCertsFromPEM(cert); !ok {
		log.Fatal("failed to append CA cert")
	}
	tlsCredentials := credentials.NewClientTLSFromCert(certPool, "")
	address := ":50051"
	// initialise our grpc connection
	conn, err := grpc.NewClient(
		address,
		//grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithTransportCredentials(tlsCredentials),
		grpc.WithUnaryInterceptor(
			func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
				log.Printf("sending request: %s", method)
				err := invoker(ctx, method, req, reply, cc, opts...)
				log.Printf("response received from server: %s", method)
				return err
			}),
	)
	if err != nil {
		log.Fatal(err)
	}
	// create a client
	client := proto.NewFileServiceClient(conn)

	http.HandleFunc("/", downloadHandler(client))

	log.Printf("starting http server on address: %s", ":8080")

	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}

func downloadHandler(client proto.FileServiceClient) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := context.Background()
		// make request to server and initialise stream
		stream, err := client.DownloadFile(ctx, &proto.DownloadFileRequest{
			Name: "gopher.jpg",
		})
		if err != nil {
			log.Fatal(err)
		}
		// create a slice to store file contents
		var fileContent []byte

		// read chunks from server and store in slice
		for {
			res, err := stream.Recv()
			if err != nil {
				if err == io.EOF {
					break
				}
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			log.Println("chunk received from server")
			fileContent = append(fileContent, res.GetContent()...)
		}

		log.Println("server stream done")
		// write our slice of bytes back to the client
		if _, err := w.Write(fileContent); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}
